/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = ibmmq_charset.c                                    */
/*  NOTE      =                                                    */
/*  DATE      = 2015/06/03 by wuwk                                 */
/*******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <iconv.h>
#include <errno.h>
#include <iconv2utf8.h>
#include <libxml/xpath.h>
#include <libxml/parser.h>

#include "frcomm.h"
#include "frmem.h"
#include "frsutl.h"
#include "frsoap_parse.h"

#define IBMMQ_TRUE                          0
#define IBMMQ_FALSE                         1

typedef struct __st_ibmmq_charset_map
{
    int i_code;
    char arr_name[32];
} Ibmmq_charset_map;

Ibmmq_charset_map arr_charset_maps[] = {
    {37, "IBM037"},
    {273, "IBM273"},
    {277, "IBM277"},
    {278, "IBM278"},
    {280, "IBM280"},
    {284, "IBM284"},
    {285, "IBM285"},
    {290, "IBM290"},
    {297, "IBM297"},
    {367, "US-ASCII"},
    {420, "IBM420"},
    {424, "IBM424"},
    {437, "IBM437"},
    {500, "IBM500"},
    {775, "IBM775"},
    {813, "ISO-8859-7"},
    {819, "ISO-8859-1"},
    {850, "IBM850"},
    {852, "IBM852"},
    {855, "IBM855"},
    {856, "IBM856"},
    {857, "IBM857"},
    {860, "IBM860"},
    {861, "IBM861"},
    {862, "IBM862"},
    {863, "IBM863"},
    {864, "IBM864"},
    {865, "IBM865"},
    {866, "IBM866"},
    {868, "IBM868"},
    {869, "IBM869"},
    {870, "IBM870"},
    {871, "IBM871"},
    {874, "IBM874"},
    {875, "IBM875"},
    {878, "KOI8-R"},
    {912, "ISO-8859-2"},
    {913, "ISO-8859-3"},
    {915, "ISO-8859-5"},
    {916, "ISO-8859-8"},
    {918, "IBM918"},
    {920, "ISO-8859-9"},
    {921, "IBM921"},
    {922, "IBM922"},
    {923, "ISO-8859-15"},
    {930, "IBM930"},
    {932, "IBM932"},
    {933, "IBM933"},
    {935, "IBM935"},
    {936, "WINDOWS-936"},
    {937, "IBM937"},
    {939, "IBM939"},
    {943, "IBM943"},
    {970, "EUC-KR"},
    {1025, "IBM1025"},
    {1026, "IBM1026"},
    {1046, "IBM1046"},
    {1047, "IBM1047"},
    {1051, "HP-ROMAN8"},
    {1089, "ISO-8859-6"},
    {1097, "IBM1097"},
    {1112, "IBM1112"},
    {1122, "IBM1122"},
    {1123, "IBM1123"},
    {1124, "IBM1124"},
    {1140, "IBM1140"},
    {1141, "IBM1141"},
    {1142, "IBM1142"},
    {1143, "IBM1143"},
    {1144, "IBM1144"},
    {1145, "IBM1145"},
    {1146, "IBM1146"},
    {1147, "IBM1147"},
    {1148, "IBM1148"},
    {1149, "IBM1149"},
    {1153, "IBM1153"},
    {1200, "UTF-16"},
    {1208, "UTF-8"},
    {1250, "WINDOWS-1250"},
    {1251, "WINDOWS-1251"},
    {1252, "WINDOWS-1252"},
    {1253, "WINDOWS-1253"},
    {1254, "WINDOWS-1254"},
    {1255, "WINDOWS-1255"},
    {1256, "WINDOWS-1256"},
    {1257, "WINDOWS-1257"},
    {1258, "WINDOWS-1258"},
    {1364, "IBM1364"},
    {1371, "IBM1371"},
    {1388, "IBM1388"},
    {1390, "IBM1390"},
    {1392, "GB18030"},
    {1399, "IBM1399"},
    {4133, "IBM037"},
    {4369, "IBM273"},
    {4373, "IBM277"},
    {4374, "IBM278"},
    {4376, "IBM280"},
    {4380, "IBM284"},
    {4381, "IBM285"},
    {4386, "IBM290"},
    {4393, "IBM297"},
    {4516, "IBM420"},
    {4520, "IBM424"},
    {4596, "IBM500"},
    {4946, "IBM850"},
    {4948, "IBM852"},
    {4951, "IBM855"},
    {4952, "IBM856"},
    {4953, "IBM857"},
    {4964, "IBM868"},
    {4965, "IBM869"},
    {4966, "IBM870"},
    {4967, "IBM871"},
    {4970, "IBM874"},
    {5014, "IBM918"},
    {5026, "IBM930"},
    {5028, "IBM932"},
    {5029, "IBM933"},
    {5031, "IBM935"},
    {5033, "IBM937"},
    {5035, "IBM939"},
    {5066, "EUC-KR"},
    {5142, "IBM1046"},
    {5143, "IBM1047"},
    {5484, "IBM1388"},
    {5488, "GB18030"},
    {5601, "EUC-KR"},
    {8229, "IBM037"},
    {8489, "IBM297"},
    {8629, "IBM437"},
    {8692, "IBM500"},
    {9047, "IBM855"},
    {9060, "IBM868"},
    {9122, "IBM930"},
    {9124, "IBM932"},
    {9125, "IBM933"},
    {9127, "IBM935"},
    {9131, "IBM939"},
    {12325, "IBM037"},
    {12725, "IBM437"},
    {12788, "IBM500"},
    {13218, "IBM930"},
    {13221, "IBM933"},
    {13223, "IBM935"},
    {13488, "UTF-16"},
    {16421, "IBM037"},
    {16821, "IBM437"},
    {16884, "IBM500"},
    {17314, "IBM930"},
    {17584, "UTF-16"},
    {20517, "IBM037"},
    {20917, "IBM437"},
    {20980, "IBM500"},
    {24613, "IBM037"},
    {25076, "IBM500"},
    {29172, "IBM500"},
    {32805, "IBM037"},
    {33268, "IBM500"},
    {33618, "IBM850"},
    {33698, "IBM930"},
    {37364, "IBM500"},
    {41460, "IBM500"},
    {45556, "IBM500"},
    {49652, "IBM500"},
    {53748, "IBM500"},
    {61696, "IBM500"},
    {61697, "IBM850"},
    {61698, "IBM850"},
    {61699, "ISO-8859-1"},
    {61710, "ISO-8859-1"},
    {61711, "IBM500"},
    {61712, "IBM500"}
};

typedef struct __st_append_str {
    char *p_str;
    unsigned int ui_len;
} Append_str;


/*******************************************************************************
*  FUNC     :  字符转码
*  ARGS     :  p_inbuffer                 (转换前字符串)
*           :  ui_inbuffer_size           (转换前字符串的长度)
*           :  p_from_charset            （元字符集）
*           :  p_to_charset               (目的字符集)
*           :  pp_output_buffer           (转换后字符串)
*           :  p_ui_output_size           (转换后字符串的长度)
*  RTN      :  0    成功
*           :  1    部分成功
*           :  2    失败
*  NOTE     :
*******************************************************************************/
int ibmmq_iconv(char *p_inbuffer
              , unsigned int ui_inbuffer_size
              , char *p_from_charset
              , char *p_to_charset
              , char **pp_output_buffer
              , unsigned int *p_ui_output_size)
{
    iconv_t fp = 0;
    size_t s_ret = 0;
    size_t s_out_left_size = 0;
    size_t s_out_size = 0;
    size_t s_in_left_size = 0;
    int i_count = 2;
    char *p_outbuf = NULL;
    char *p_output_tmp = NULL;
    char *p_input_tmp = NULL;

    /* 入力为空 */
    if (p_inbuffer == NULL || ui_inbuffer_size == 0)
    {
        return 0;
    }
    /* 申请一个字符集转换的描述符，从from charset到to charset */
    fp = iconv_open(p_to_charset, p_from_charset);
    /* 申请描述符失败 */
    if (fp == 0)
    {
        frdbg("iconv_open failed\n");
        return 2;
    }

    /* 做iconv转换 */
    while (1)
    {
        /* 申请一块出力buffer用的内存 */
        s_out_size = ui_inbuffer_size * i_count + 1;
        p_outbuf = (char *)frmalloc(s_out_size);
        /* 申请内存出错 */
        if (p_outbuf == NULL)
        {
            frdbg("malloc failed\n");
            iconv_close(fp);
            return 2;
        }

        p_output_tmp = p_outbuf;
        p_input_tmp = p_inbuffer;
        s_in_left_size = ui_inbuffer_size;
        s_out_left_size = s_out_size;
        errno = 0;
        s_ret = iconv(fp, &p_input_tmp, &s_in_left_size, &p_output_tmp, &s_out_left_size);
        /* iconv出错 */
        if (s_ret != 0)
        {
            /* 出力buffer内存不足 */
            if (errno == E2BIG)
            {
                frdbg("Output memory is not enough\n");
                i_count++;
                frfree((void *)p_outbuf);
                p_outbuf = NULL;
                continue;
            }
            /* 有不可转换字符只取前面可转的那段 */
            else if (errno == EILSEQ)
            {
                frdbg("There has illegal byte sequence\n");
                p_outbuf[s_out_size - s_out_left_size] = '\0';
                *pp_output_buffer = p_outbuf;
                *p_ui_output_size = s_out_size - s_out_left_size;
                iconv_close(fp);
                return 1;
            }
            else
            {
                frfree((void *)p_outbuf);
                iconv_close(fp);
                return 2;
            }
        }
        else
        {
            break;
        }
    }

    *pp_output_buffer = p_outbuf;
    p_outbuf[s_out_size - s_out_left_size] = '\0';
    *p_ui_output_size = s_out_size - s_out_left_size;
    iconv_close(fp);
    return 0;
}

/*******************************************************************************
*  FUNC     :  用二分法查找charset maps
*  ARGS     :  key                               (charset编号)
*  RTN      :  -1                                (未找到)
*           index                                (找到的下标)
*  NOTE     :
*******************************************************************************/
FR_STATIC int __binary_search(int key)
{
    int len = sizeof(arr_charset_maps) / sizeof(arr_charset_maps[0]);
    int mid = 0;
    int front = 0;
    int back = len - 1;
    while (front <= back)
    {
        mid = (front + back) / 2;
        if (arr_charset_maps[mid].i_code == key)
            return mid;
        if (arr_charset_maps[mid].i_code < key)
            front = mid + 1;
        else
            back = mid - 1;
    }
    return -1;
}

/*******************************************************************************
*  FUNC     :  判断是否是c2,c3字符串
*  ARGS     :  p_data                        (入力字符串)
*           :  ui_data_len                   (入力字符串长度)
*  RTN      :  IBMMQ_TRUE
*              IBMMQ_FALSE
*  NOTE     :
*******************************************************************************/
FR_STATIC int ibmmq_is_magiced_by_c2c3(char *p_data, unsigned int ui_data_len)
{
    int i = 0;

    if (p_data == NULL || ui_data_len < 2)
        return IBMMQ_FALSE;

    for (; i < int(ui_data_len - 2); i++)
    {
        if (((unsigned char)*(p_data+i) == 0xC2U || (unsigned char)*(p_data+i) == 0xC3U)
            && ((unsigned char)*(p_data+i+2) == 0xC2U || (unsigned char)*(p_data+i+2) == 0xC3U))
        {
            return IBMMQ_TRUE;
        }
    }

    return IBMMQ_FALSE;
}

FR_STATIC void __ibmmq_append_free(Append_str *p_st_append)
{
    if (p_st_append == NULL)
        return;

    frfree(p_st_append->p_str);
    frfree(p_st_append);

    return;
}

FR_STATIC int __ibmmq_append(Append_str **pp_st_append
                           , char *p_data
                           , unsigned int ui_len)
{
    Append_str *p_st_append = NULL;
    Append_str *p_st_in = NULL;
    char *p_str_append = NULL;
    unsigned int ui_append = 0;

    if (p_data == NULL)
        return FR_FAIL;

    p_st_in = *pp_st_append;
    if (p_st_in == NULL)
    {
        p_st_append = (Append_str *)frmalloc(sizeof(Append_str));
        if (p_st_append == NULL)
            return FR_FAIL;
        memset(p_st_append, 0x00, sizeof(Append_str));

        p_st_append->p_str = (char *)frmalloc(ui_len + 1);
        if (p_st_append->p_str == NULL)
        {
            frfree(p_st_append);
            return FR_FAIL;
        }
        memset(p_st_append->p_str, 0x00, ui_len + 1);

        memcpy(p_st_append->p_str, p_data, ui_len);
        p_st_append->ui_len = ui_len;

        *pp_st_append = p_st_append;
        return FR_SUCC;
    }

    ui_append = ui_len + p_st_in->ui_len;
    p_str_append = (char *)frmalloc(ui_append + 1);
    if (p_str_append == NULL)
        return FR_FAIL;
    memset(p_str_append, 0x00, ui_append + 1);

    memcpy(p_str_append, p_st_in->p_str, p_st_in->ui_len);
    memcpy(p_str_append+p_st_in->ui_len, p_data, ui_len);

    frfree(p_st_in->p_str);
    p_st_in->p_str = p_str_append;
    p_st_in->ui_len = ui_append;

    return FR_SUCC;
}

FR_STATIC int __ibmmq_get_xml_len(char *p_xml, unsigned int ui_len, unsigned int *p_len)
{
    xmlDocPtr p_st_doc = NULL;
    xmlNodePtr p_st_root = NULL;
    char arr_root[64];
    char *p_end = NULL;

    if (p_xml == NULL)
        return FR_FAIL;

    p_st_doc = xmlReadMemory(p_xml, ui_len, NULL, NULL, XML_PARSE_NOBLANKS|XML_PARSE_NOERROR|XML_PARSE_NOWARNING);
    if (p_st_doc == NULL)
        goto err;

    /* 根节点 */
    p_st_root = xmlDocGetRootElement(p_st_doc);
    if (p_st_root == NULL)
        goto err;

    snprintf(arr_root, sizeof(arr_root), "</%s>\n", p_st_root->name);
    p_end = frmemstr(p_xml, ui_len, arr_root, strlen(arr_root));
    if (p_end == NULL)
        goto err;

    p_end += strlen(arr_root);
    *p_len = p_end - p_xml;

    xmlFreeDoc(p_st_doc);
    return FR_SUCC;

err:
    xmlFreeDoc(p_st_doc);
    return FR_FAIL;
}

FR_STATIC int __ibmmq_get_encoding(char *p_xml, unsigned int ui_len, char *p_encoding)
{
    char *p_tmp = NULL;
    char *p_tmp2 = NULL;

    if (p_xml == NULL)
        return FR_FAIL;

    p_tmp = frmemstr(p_xml, ui_len, (char *)"encoding=\"", 10);
    if (p_tmp == NULL)
        return FR_FAIL;

    p_tmp += 10;
    p_tmp2 = frmemstr(p_tmp, ui_len, (char *)"\"", 1);
    if (p_tmp2 == NULL)
        return FR_FAIL;

    strncpy(p_encoding, p_tmp, p_tmp2-p_tmp);
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  把xml数据和其他数据分开做iconv
*  ARGS     :  *p_buff_in                       (输入的buffer)
*           :  ui_buff_in_len                   (输入的buffer长度)
*           :  p_fromcharset                    (源charset)
*           :  **pp_out                         (输出的buffer)
*           :  *p_ui_out_len                    (输出的buffer长度)
*  RTN      :  FR_FAIL                          (FR_SUCC)
*              FR_SUCC                          (FR_FAIL)
*  NOTE     :  xml数据用encoding的值做转换
*******************************************************************************/
FR_STATIC int __ibmmq_xml_iconv(char *p_buff_in
                              , unsigned int ui_buff_in_len
                              , char *p_fromcharset
                              , char **pp_out
                              , unsigned int *p_ui_out_len)
{
    Append_str *p_st_append = NULL;
    char *p_xml_index = NULL;
    char *p_out_buff = NULL;
    char *p_in = NULL;
    char *p_out = NULL;
    char arr_encoding[64];
    unsigned int ui_in_len = 0;
    unsigned int ui_xml_len = 0;
    unsigned int ui_out_len = 0;
    unsigned int ui_len_tmp = 0;
    int i_ret = 0;

    if (p_buff_in == NULL || p_fromcharset == NULL || pp_out == NULL || p_ui_out_len == NULL)
        return FR_FAIL;

    p_in = p_buff_in;
    ui_in_len = ui_buff_in_len;

    /* 检查里面是否有xml数据 */
    while (1)
    {
        if (ui_in_len > 6)
        {
            p_xml_index = frmemstr(p_in, ui_in_len, (char *)"<?xml ", 6);
            if (p_xml_index == NULL)
                break;
        }
        else
            break;

        ui_xml_len = ui_in_len - (p_xml_index - p_in);
        /* 非xml */
        if (frxml_detect(p_xml_index, ui_xml_len) == FR_FAIL)
            break;

        /* xml前有数据 */
        if (p_xml_index != p_buff_in)
        {
            i_ret = iconv_to_utf8(p_in, p_xml_index - p_in
                                , p_fromcharset, &p_out_buff, &ui_out_len);
            if (i_ret == 2)
                goto err;
            else if (i_ret == 1)
            {
                frfree(p_out_buff);
                goto end;
            }

            i_ret = __ibmmq_append(&p_st_append, p_out_buff, ui_out_len);
            if (i_ret == FR_FAIL)
                goto err;

            frfree(p_out_buff);
            p_out_buff = NULL;

            ui_in_len -= (p_xml_index - p_in);
            p_in += (p_xml_index - p_in);
        }

        if (__ibmmq_get_xml_len(p_xml_index, ui_xml_len, &ui_len_tmp) == FR_FAIL)
            goto err;

        /* xml数据转换 */
        memset(arr_encoding, 0x00, sizeof(arr_encoding));
        if (__ibmmq_get_encoding(p_xml_index, ui_len_tmp, arr_encoding) == FR_FAIL)
            goto err;
        i_ret = iconv_to_utf8(p_xml_index, ui_len_tmp
                            , arr_encoding, &p_out_buff, &ui_out_len);
        if (i_ret == 2)
            goto err;
        else if (i_ret == 1)
        {
            frfree(p_out_buff);
            goto end;
        }

        i_ret = __ibmmq_append(&p_st_append, p_out_buff, ui_out_len);
        if (i_ret == FR_FAIL)
            goto err;

        frfree(p_out_buff);
        p_out_buff = NULL;

        p_in += ui_len_tmp;
        ui_in_len -= ui_len_tmp;
    }

    if (ui_in_len != 0)
    {
        i_ret = iconv_to_utf8(p_in, ui_in_len, p_fromcharset, &p_out_buff, &ui_out_len);
        if (i_ret == 2)
            goto err;
        else if (i_ret == 1)
        {
            frfree(p_out_buff);
            goto end;
        }
        i_ret = __ibmmq_append(&p_st_append, p_out_buff, ui_out_len);
        if (i_ret == FR_FAIL)
            goto err;

        frfree(p_out_buff);
        p_out_buff = NULL;
    }

    *pp_out = p_st_append->p_str;
    *p_ui_out_len = p_st_append->ui_len;

    frfree(p_st_append);

    return FR_SUCC;
end:
    if (p_st_append)
        __ibmmq_append_free(p_st_append);

    p_out = (char *)frmalloc(ui_buff_in_len + 1);
    if (p_out == NULL)
        return FR_FAIL;
    memset(p_out, 0x00, ui_buff_in_len + 1);

    memcpy(p_out, p_buff_in, ui_buff_in_len);
    *pp_out = p_out;
    *p_ui_out_len = ui_buff_in_len;

    return FR_SUCC;
err:
    if (p_out_buff)
        frfree(p_out_buff);
    if (p_st_append)
        __ibmmq_append_free(p_st_append);
    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  字符串转UTF8
*  ARGS     :  s_charset                        (charset编号)
*           :  *p_buff_in                       (输入的buffer)
*           :  ui_buff_in_len                   (输入的buffer长度)
*           :  **pp_buff_out                    (输出的buffer)
*           :  *p_ui_buff_out_len               (输出的buffer长度)
*  RTN      :  0                                    (FR_SUCC)
*              1                                    (FR_FAIL)
*  NOTE     :  如果没找到对应的charset或者原来就是UTF-8，把原数据拷到出力buffer
*******************************************************************************/
int ibmmq_str_to_utf8(short s_charset
                    , char *p_buff_in
                    , unsigned int ui_buff_in_len
                    , char **pp_buff_out
                    , unsigned int *p_ui_buff_out_len)
{
    int i_index = 0;
    int i_ret = 0;
    unsigned int ui_tmp = 0;
    char *p_buff_out = NULL;
    char *p_buff_tmp = NULL;

    if (p_buff_in == NULL || ui_buff_in_len == 0
        || pp_buff_out == NULL || p_ui_buff_out_len == NULL)
        return FR_FAIL;

    /* 如果是c2c3字符串，先从utf-8转成iso-8859-1,再从gb2312转成utf-8,失败就保留原有数据 */
    if (ibmmq_is_magiced_by_c2c3(p_buff_in, ui_buff_in_len) == IBMMQ_TRUE)
    {
        /* 从utf-8转成iso-8859-1 */
        i_ret = ibmmq_iconv(p_buff_in, ui_buff_in_len
                          , (char *)"UTF-8", (char *)"ISO-8859-1"
                          , &p_buff_tmp, &ui_tmp);
        if (i_ret == 2)
            return FR_FAIL;
        else if (i_ret == 1)
        {
            frfree(p_buff_tmp);
            goto end;
        }

        /* 从gb2312转成utf-8 */
        i_ret = iconv_to_utf8(p_buff_tmp, ui_tmp, (char *)"GB2312"
                            , pp_buff_out, p_ui_buff_out_len);
        frfree(p_buff_tmp);
        if (i_ret == 2)
            return FR_FAIL;
        else if (i_ret == 1)
        {
            frfree(*pp_buff_out);
            goto end;
        }

        return FR_SUCC;
    }

    /* 不是c2c3字符串 */
    if ((i_index = __binary_search(s_charset)) != -1)
    {
        /* 如果charset是UTF-8, 就不做转换 */
        if (strncasecmp(arr_charset_maps[i_index].arr_name, "UTF-8", 5) == 0)
            goto end;

        i_ret = __ibmmq_xml_iconv(p_buff_in, ui_buff_in_len, arr_charset_maps[i_index].arr_name
                                                 , pp_buff_out, p_ui_buff_out_len);
        return i_ret;
    }

end:
    p_buff_out = (char *)frmalloc(ui_buff_in_len + 1);
    memcpy(p_buff_out, p_buff_in, ui_buff_in_len);
    p_buff_out[ui_buff_in_len] = '\0';

    *pp_buff_out = p_buff_out;

    return FR_SUCC;
}
